#pragma once
#include <vector>
#include <queue>
#include "thread.hpp"
#include "Mutex.hpp"
#include "Log.hpp"
#include "Cond.hpp"
#include <functional>
#include <mutex>
int gnum = 5;

namespace kzz
{
    // T类型的任务
    template <class T>
    class ThreadPool
    {
    public:
        ThreadPool(int num = gnum)
            : _num(num), _sleepernum(0) // 初始等待线程
              ,
              _isrunning(false) // 线程池是否启动
        {
            for (int i = 0; i < _num; i++)
            {
                // 先初始化每一个线程，这个时候还没有真的创建线程，只是先把池里的每一个线程初始化
                // 从任务队列里拿任务，
                // 县城要做的事就是从任务队列里拿任务
                _threads.emplace_back([this]()
                                      { handletask(); });
            }
        }

        void WakeUpAllThread()
        {
            kzz::LockGuard lock(_mutex);

            if (_sleepernum > 0)
            {
                LOG(kzz::LogLevel::WORING) << "线程睡眠数量大于一";
                _cond.Broakcast();
            }
        }

        void WakeUpOne()
        {
            _cond.Signal();
        }

        void Start()
        {
            if (_isrunning)
                return;

            _isrunning = true;
            for (int i = 0; i < _num; i++)
            {
                _threads[i].Start();
            }
        }

        void Stop()
        {
            if (!_isrunning)
                return;
            _isrunning = false;
            //
            // 如果还有正在等待的进程就唤醒他们去去任务，这个时候县城池已经是不运行状态了，不能在入队列了

            WakeUpAllThread();

            // 不能直接终止各线程，要保证任务队列里没有遗留线程
            // for (int i = 0; i < _num; i++)
            //{
            //    _threads[i].stop();
            //}
        }
        void Join()
        {
            for (int i = 0; i < _num; i++)
            {
                _threads[i].Join();
            }
        }

        ////传入任务
        //
        bool Equeue(const T &in)
        {
            // 线程池必须是运行状态，才能往里写，否则会有任务遗留

            if (_isrunning)
            {
                kzz::LockGuard lock(_mutex);
                //////////////////////////////////////////////////////////////////////
                char thread_name[16] = {0}; // 通常最大长度为16
                pthread_getname_np(pthread_self(), thread_name, sizeof(thread_name));
                printf("Current thread name: %s,生产者枪锁成功\n", thread_name);
                /////////////////////////////////////////////////////////////
                // while(_taskq.size()>=5)
                //  _cond.Wait(_mutex);

                _taskq.push(in);
                LOG(kzz::LogLevel::WORING) << "现有任务数" << _taskq.size();

                // LOG(kzz::LogLevel::WORING) << "push任务成功，任务为："<<in;
                //  这时候肯定是有任务，如果所有进程都在睡觉，就叫醒一个去干活
                if (_sleepernum > 0)
                {
                    //_cond.Broakcast();
                     WakeUpOne();
                }

                return true;
            }
            return false;
        }

        // 每个线程的任务都是从队列那任务，然后执行
        void handletask()
        {
            while (true)
            {
                T t;
                // if (true)
                {
                    //  LOG(kzz::LogLevel::WORING)<< "开始抢锁：" << t ;
                    kzz::LockGuard lock(_mutex);
                    //////////////////////////////////////////////////////////////////////
                    char thread_name[16] = {0}; // 通常最大长度为16
                    pthread_getname_np(pthread_self(), thread_name, sizeof(thread_name));
                    printf("Current thread name: %s,枪锁成功\n", thread_name);
                    //////////////////////////////////////////////////////////////////////
                    // LOG(kzz::LogLevel::WORING)<< "抢锁成功" << t ;
                    // 队列为空，并且还是运行状态，可以去等待任务的插入
                    while (_taskq.empty() && _isrunning)
                    {

                        LOG(kzz::LogLevel::WORING) << "本线程将要睡眠，当前已经睡眠数量为：" << _sleepernum;
                        _sleepernum++;
                        _cond.Wait(_mutex);
                        _sleepernum--;
                        // LOG(kzz::LogLevel::WORING)<<"本线程醒来，将要重新判断是否有任务，当前已经睡眠数量为："<<_sleepernum;
                    }
                    // 这里很重要，如果是不运行状态并且队列已经空了，可以直接结束线程了
                    if (!_isrunning && _taskq.empty())
                        break;

                    // 走到这里队列一定不为空，可能是运行状态还有任务，
                    // 也有可能是不运行状态，但是还有任务，也要把剩余的任务全都取出来
                    //  等完了，并且也有锁，一定是有任务的
                    //  把任务取走

                    t = _taskq.front();

                    _taskq.pop();
                }
                // 现在已经获得任务了，不需要锁了，在锁外面执行任务
                 t();
                //LOG(kzz::LogLevel::WORING) << "获取任务成功，：" << t;
            }
        }
        static ThreadPool<T> *GetInstance()
        {
            if (inst == nullptr)
            {
                lock.lock(); // 防止多线程同时创建线程池
                if (inst == nullptr)
                {
                    inst = new ThreadPool<T>;
                    LOG(LogLevel::DBUGER)<<"获取单例成功！";
                   // inst->Start();
                }
                lock.unlock();
            }
            return inst;
        }
        ThreadPool<T>&  operator=(const ThreadPool<T>& p)=default;
        ThreadPool(const ThreadPool<T>& p)=default;


    private:
        std::vector<kzz::Thread> _threads;
        int _num; // 线程池中线程的个数

        std::queue<T> _taskq; // 任务队列

        kzz::Mutex _mutex; // 给任务队列用，临界资源，取放不在一块   等待线程数和任务队列都是临界资源
        kzz::Cond _cond;   // 信号量，控制线程切换等待

        int _sleepernum; // 正在信号量处等待获取任务的线程数
        bool _isrunning; // 线程池是否启动
        ////////////////////////////////////////
        static ThreadPool<T> *inst;
        static std::mutex lock;
    };
    template <class T>
    ThreadPool<T> *ThreadPool<T>::inst = nullptr;
    template <class T>
    std::mutex ThreadPool<T>::lock;
}